﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NeurOthello.NNetwork
{
    [Serializable]
    class Neuron
    {
        protected static Random rand = new Random();
        protected double[] weights, inputs;
        protected Neuron[] outputs;
        protected int nInLayer, nInputs;
        protected double bias, Err, activationInput, signal;
        public int NWeights { get { return weights.Length + 1; } }

        public Neuron(int nIn, int nInLayer, Neuron[] outputs)
        {
            this.nInLayer = nInLayer;
            this.outputs = outputs;
            this.nInputs = nIn;
 
            inputs = new double[nIn];
            weights = new double[nIn];
            for (int i = 0; i < nIn; i++)
                weights[i] = -0.5+rand.NextDouble();
            bias = -0.5+rand.NextDouble();           
        }

        public virtual void Activate()
        {
            InputFunction();
            signal = Sigmoid.Sig(activationInput);
            int i = 0;
            foreach (Neuron send in outputs)
            {
                send.RecieveInput(signal, nInLayer);
                i++;
            }
        }

        public void RecieveInput(double input, int from)
        {
            inputs[from] = input;
        }

        protected void InputFunction()
        {
            double sum = 0;
            for (int i = 0; i < nInputs; i++)
                sum += inputs[i] * weights[i];
            sum += bias;
            activationInput = sum;            
        }

        public void CalcNodeBlame()
        {
            Err = 0;
            for (int i = 0; i < outputs.Length; i++)
                 Err += outputs[i].GetNodeBlame(nInLayer);
        }

        public double GetNodeBlame(int n)
        {
            return weights[n] * Err;
        }

        
        public void AdjustWeights(double LearningR)
        {
            double sig = Sigmoid.Sig(activationInput);
            for (int i = 0; i < weights.Length; i++)
            {
                weights[i] += LearningR * inputs[i] * Err * Sigmoid.SigPrim(sig);
            }

            bias += LearningR * Err * Sigmoid.SigPrim(sig);
        }

        public string GetNeuronLine()
        {
            string line = "n";
            line += bias;
            if (weights != null)
                for (int i = 0; i < weights.Length; i++)
                    line += "." + weights[i];
            return line;
        }

        public void ReadNeuronLine(string line)
        {
            string[] warr = line.Split('.');
            bias = Convert.ToDouble(warr[0]);
            if (weights != null)
                for (int i = 0; i < weights.Length; i++)
                    weights[i] = Convert.ToDouble(warr[i + 1]);
        }

        public byte[] GetNeuronByteGene()
        {
            byte[] gene = new byte[weights.Length+1];

            if (weights != null)
            {
                gene[0] = WeightToByte(bias);
                for (int i = 0; i < weights.Length; i++)
                    gene[i+1] = WeightToByte(weights[i]);
            }
            return gene;
        }

        public void ReadNeuronGene(byte[] gene)
        {
            if (weights != null)
            {
                bias = ByteToWeight(gene[0]);
                for (int i = 0; i < weights.Length; i++)
                    weights[i] = ByteToWeight(gene[i + 1]);
            }

        }

        public double ByteToWeight(byte b)
        {
            return (double)b / 127.5 - (double)1;
        }

        public byte WeightToByte(double w)
        {
            double t = (w + 1) * 127.5;
            return (byte)Math.Round(t, 0);
        }

    }
}
